﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Spring.Objects.Factory.Config;

namespace Strongshell.Recoil.Core.Composition.Workflow
{
    /// <summary>
    /// Fluent interface for configuring an object definition.
    /// </summary>
    /// <typeparam name="TTarget">The type of the object.</typeparam>
    public interface IDefinitionFragment<TTarget> : INamedDefinitionFragment
    {
        /// <summary>
        /// Sets the object definition name to be generated from the supplied generic type parameter. Useful in conjunction
        /// with using the Wire() method to avoid having to provide a string based object name.
        /// </summary>
        /// <typeparam name="TInterface">The type from wich the object's name should be generated.</typeparam>
        /// <returns>Fluent interface for configuring the object definition.</returns>
        IDefinitionFragment<TTarget> As<TInterface>();

        /// <summary>
        /// Sets the object definition name to the supplied string.
        /// </summary>
        /// <param name="name">The object name.</param>
        /// <returns>Fluent interface for configuring the object definition.</returns>
        IDefinitionFragment<TTarget> As(string name);

        /// <summary>
        /// Adds an alias to the object configuration. The alias will be generated from the supplied generic
        /// type parameter. Useful for registering an object with the names of multiple interfaces that it
        /// implements. The object can then be easily injected using the Wire() with all
        /// the interfaces that have aliases defined.
        /// </summary>
        /// <typeparam name="TInterface">The type from wich the object's alias should be defined.</typeparam>
        /// <returns>Fluent interface for configuring the object definition.</returns>
        IDefinitionFragment<TTarget> Alias<TInterface>();

        /// <summary>
        /// Adds the supplied alias to the object configuration.
        /// </summary>
        /// <param name="name">The new alias to add.</param>
        /// <returns>Fluent interface for configuring the object definition.</returns>
        IDefinitionFragment<TTarget> Alias(string name);

        /// <summary>
        /// Sets the object to be a instantiated as a singleton. Spring.NET will keep only one instance
        /// of this object in the container.
        /// </summary>
        /// <returns>Fluent interface for configuring the object definition.</returns>
        IDefinitionFragment<TTarget> AsSingleton();

        /// <summary>
        /// Sets the object to be instantiated as a prototype. Spring.NET will construct a new instance for each
        /// dependency.
        /// </summary>
        /// <returns></returns>
        IDefinitionFragment<TTarget> AsPrototype();

        /// <summary>
        /// Sets whether the object will be initialized lazily.
        /// </summary>
        /// <param name="enable">Set to TRUE if the object should be lazy initialized, FALSE otherwise.</param>
        /// <returns>Fluent interface for configuring the object definition.</returns>
        IDefinitionFragment<TTarget> LazyInit(bool enable);

        /// <summary>
        /// Sets the autowiring mode for the object.
        /// </summary>
        /// <param name="wiringMode">The <see cref="AutoWiringMode"/> to use for this object.</param>
        /// <returns>Fluent interface for configuring the object definition.</returns>
        IDefinitionFragment<TTarget> AutoWire(AutoWiringMode wiringMode);

        /// <summary>
        /// Selects a property to be wired using Spring.NET. Only properties and fields are allowed, selecting
        /// any other member in the <see cref="MemberExpression"/> will result in a <see cref="ArgumentException"/>
        /// being thrown.
        /// 
        /// Usage is:
        /// 
        /// Set(o => o.MyProperty)
        /// </summary>
        /// <typeparam name="TProperty">The type of the property to be wired.</typeparam>
        /// <param name="selector">The expression that selects the property.</param>
        /// <returns>Fluent interface for wiring the property.</returns>
        IMappingFragment<TTarget,TProperty> Set<TProperty>(Expression<Func<TTarget, TProperty>> selector);

        /// <summary>
        /// Selects a method to be called for initialization using Spring.NET. Only methods are allowed, selecting
        /// any other member in the <see cref="MemberExpression"/> will result in a <see cref="ArgumentException"/>
        /// being thrown.
        /// 
        /// Usage is:
        /// 
        /// Set(o => o.Init())
        /// </summary>
        /// <param name="selector">The expression that calls the initialization method.</param>
        /// <returns>Fluent interface for configuring the object definition.</returns>
        IDefinitionFragment<TTarget> Init(Expression<Action<TTarget>> selector);
    }
}